home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
The World of Computer Software.iso
/
oobpls10.zip
/
OOBPLUS.DOC
< prev
next >
Wrap
Text File
|
1992-11-06
|
80KB
|
1,609 lines
OOBPLUS - CompuServe B protocol objects
Copyright (c) Steve Sneed 1991
All rights reserved
This document and the included source code ("the material") are the
copyrighted work of their author. The material is provided to TurboPower
Software, and a license is hereby granted to TurboPower Software, for
distribution with their product(s). Purchasers of TurboPower Software
product(s) that include the material may use the material under the same
purchase agreement rights and restrictions as the TurboPower product(s).
Portions of the material are copyright (c) by CompuServe, Inc. and
are used by permission. "GIF" and "Graphics Interchange Format" are
Service Marks (sm) of CompuServe, Inc., an H&R Block Company.
---- Other Documentation --------------------------------------------------
The information and code provided here is based on the BPlus spec
document, and the GIF 87a and 89a spec documents. For anything more than
a direct "plug-n-play" implementation based on these objects you will
definitely need those documents, and both are available for download from
CompuServe. The BPlus document is available in the IBMCOM Forum's LIBrary
10 (Protocols) as PBDOC.ARC. The GIF 89a specification, which includes
all of the 87a spec, is available in the PICS Forum's LIBrary 14 (Misc.
Utilities & Code) as GIF89A.DOC. (Other word processor formats, such as
WordPerfect and MSWord, are available for the GIF 89a spec; BROwse the
library with a KEYword of "89A" or "SPEC" to find them.)
The book "Programmer's Guide to PC & PS/2 Video Systems" (Richard
Wilton, Microsoft Press 1988, ISBN 1-55615-103-9) is generally considered
the "bible" of PC video programming. Highly recommended if you intend to
work with GIF decoding. I have also found "C Programmer's Guide to Serial
Communications" (Joe Campbell, Howard W. Sams & Company 1987, ISBN
0-672-22584-0) to be extremely helpful in general comm programming and
protocols.
---- Overview -------------------------------------------------------------
The CompuServe Information Service (CIS) is the largest and most popular
system of its type in the world, with over 1,000,000 members in the US and
abroad. CIS provides several "special" capabilities, such as the ability
to display graphics in real time and the ability to encapsulate the entire
online session within a protocol for improved data integrity and management.
Many of these special capabilities are based on CompuServe's own transport
protocol, the "B" protocol. Because of the size and number of users of CIS
today, any communications program should provide support for at least the
basic File Transfer Protocol (FTP) portions of the B protocol. OOBPLUS
makes providing that support simple.
The OOBPLUS unit provides a set of objects for B protocol. Derived from
Async Professional's AbstractProtocol object, OOBPLUS provides a "plug-in"
interface for both the B series protocol's FTP services, and the special
version for use with CompuServe's GIF graphics image display services.
There are three distinct species of B protocol in use today:
"Classic" B: The original B protocol. This version defines a protocol
similar in some ways to the XMODEM protocol but with special consider-
ation for the packet-switcher network CIS uses to connect users to their
hosts. It uses a 512-byte block at all bps rates, a one-byte arithmetic
checksum, and simple "send block/wait for ACK" flow of control. It does
allow for flexible block length of less than 512 bytes, and uses a fixed
quoting set (see below). Classic B provides a much more robust protocol
environment than XMODEM for CIS, but its thruput is only slightly
better than XMODEM. Classic B was designed in the days of 300baud
modems and early protocol technology.
"Quick" B: Quick B (QB) is a major enhancement over Classic B. While
maintaining backwards compatibility with Classic B it adds "send-ahead"
technology (the ability to send more than one packet before receiving an
acknowledgement), a larger 1K packet size for better thruput with higher
speed modems, and 16-bit CRC blockchecking. QB is designed so the host
can "handshake" with the remote and determine whether Classic B or QB
will be used. QB was such a marked improvement over Classic B that many
comm programs rushed to support it, and some unfortunately did not
implement the handshake properly; as a result CIS had to add a new
option to all of its protocols menus just for QB.
"BPlus": BPlus (B+) is a superset of QB, or more correctly, QB is a
subset of B+. QB was more a preliminary version of B+ than a
self-standing version, and only the rapid implementation of QB by
several major comm programs kept it in use. B+ defines several
extensions and enhancements over QB, such as the "TI" file information
packet type. B+ also introduced the use of flexible maximum packet
size, to allow achievement of optimum packet size based on baud rate and
other factors. Most importantly for users, B+ defines a method to
resume an aborted download from the point of abort in the transferred
file rather than forcing the download to restart from the beginning.
CompuServe would like to see QB disappear completely and is encouraging
developers to provide complete B+ support; many major comm programs
(such a ProComm Plus 2.0 and CrossTalk XVI and Mk 4) have done so, but a
large base of comm programs are still in use that only provide QB
support (such as BitCom, which many modem vendors distribute free with
their modems).
B+ is not a file transfer protocol in the typical sense. It is a
"Transport Level" protocol, and as such can be used to provide protocol
error trapping and data management to an entire online session which may
include file transfer sub-sessions. The full B+ specification defines
multiple "layers" of protocol support, the lowest being the "Transport
Layer". The transport layer provides the basic data packetization, flow
control and error handling. On top of the Transport Layer may be
"Application-Specific" layer(s) which performs preprocessing (such as
decompression of compressed data) or prioritizing/directing of data to
various parts of the overall program. In order to use B+ as a full
transport protocol, the CIS host and the local program must handshake
immediately after the remote logs into the service, both for protocol
support and then for application support. This requires the remote
software to have intimate knowledge of the host and vice versa, so this
level of B+ is currently only used by software developed by CIS itself. B
was originally developed for the Vidtex terminals back in the late 70's and
early 80's, but currently the only software in the PC arena that uses
full-session protocol encapsulation is the Host-Micro Interface protocol in
the Compuserve Information Manager products for PCs and Macs.
HMI NOTE: This new version of OOBPLUS now specifically includes the
modifications nessessary to support the special HMI version of B+. However,
because no documentation has been made publicly available by CompuServe on
the exact format or usage of HMI, what's here has been arrived at by
experimentation and may require modification as more information on HMI
becomes available.
The B+ protocol was designed with more than PCs in mind. Many types of
computers access the CIS hosts, from Apple ]['s and RadioShack Model III's
to Cray supercomputers. Also, CIS ties to several different telephone
networks both in the US and abroad, and many of these networks are
decidedly different from the network CIS itself owns and operates
(especially the overseas PADs and networks). Because many of these
networks use some control characters for their own internal purposes, and
many of the mini and mainframe computers have their own interpretation of
certain characters, B+ "quotes" some characters in a manner similar to
Kermit. This gives the protocol greater flexibility, but has the negative
effect of increasing packet overhead and slowing overall data thruput.
Classic B defined a fixed quoting set of the first 32 ASCII characters, but
this proved to be excessive for most microcomputers. B+ allows the quoting
set to be negotiated during protocol session startup, so that the smallest
nessessary set is used to maximize thruput.
One of the most common questions I've heard about B over the years is,
"How do I add it to my PC-based BBS?" I cannot recommend using B+ anywhere
but on CIS for typical FTP uses. B+ is optimized for CIS' packet-switched
networks and is the best protocol to use by far in that environment, but on
direct PC-to-PC links the high per-packet overhead of B+ causes thruput
to suffer dramatically. Also, a B+ "host-side" program can be difficult
to interface to current BBS technology for forking protocol modules,
because the filename prompting and startup process is different from the
common protocols like XMODEM. For BBS use, you are much better off to use
ZModem. B+ would, however, make a good base protocol for a transaction
processing system or similar project that used serial links to other
machines (I have used a modified version of it for such a system between a
main VME-based processor and AT workstations.) Earlier beta versions of
this library provided a host-side object derived from the BPProtoFTP
object, but recent changes in CIS policy would require each person
receiving this library to obtain a (no-cost) license directly from CIS
before receiving the source or working TPU so it has been eliminated.
(I've tried to remove all references to the BPProtoHost object throughout
this document; if I've missed some please forgive me.)
---- Specifics ---------------------------------------------------------------
OOBPLUS implements the B protocols in a layered fashion, just as the CIS
hosts. An abstract parent object handles the basic flow management and
packetization chores, while child objects implement the specific FTP and
GIF receive services. A hierarchial diagram:
AbstractProtocol
├── (...other derived APRO protocols...)
└── BPProtocol {abstract BPlus parent}
├── BPProtoFTP {File Transfer services}
└── BPProtoGIF {GIF receive/decode services}
The BPProtocol abstract parent is derived from Async Professional's
AbstractProtocol object to take advantage of some of its variables and
methods. Like other protocols derived from AbstractProtocol, BPProtoGIF
can be instantiated "on the fly", as it is needed. However, the
BPProtoFTP object needs to be instantiated whenever the main program is
receiving and processing characters from the commport.
Because B is a transport level protocol, the host may send non-protocol
data between protocol packets, or between the protocol support handshake
and the first actual protocol packet. The term "protocol session" is used
to denote some period of host-remote activity that begins with the
handshake and ends with with a termination packet. A FTP or GIF-receive
session may be a complete protocol session, or there may be other data
and/or commands issued via protocol packets. Some of the variables used in
the BPProtocol object and its children are "session dependent" and are
initialized whenever the handshake is performed, while others are
re-initialized each time a FTP or GIF-receive subsession is started.
B+ requires an 8-bit data path. The CIS hosts default to E71 port
parameters when you connect to the host, so your terminal handler must be
able to switch to N81 "on the fly" for a protocol session, and back to E71
when the session is complete. Such a flying switch, with data streaming
into the port, is tricky to do without dropping characters. The SetLine
method in Async Professional's AbstractPort object can be used to make the
switch.
Considerations for protocol handshake and initialization
--------------------------------------------------------
Most conventional protocols, such as XMODEM, are considered "receiver
driven" because whichever end of the protocol link is receiving the primary
data flow manages the protocol. B+ on the other hand is considered "host
driven", because one end of the link is the "master" or host while the
other is the "slave" or remote, irrespective of the direction of primary
data flow. On CIS, the host is always the CIS computer, with the local
PC system being the remote.
Starting XMODEM is typically a two-step process: you start XMODEM on the
other end of the link, and then you start it on the local end. B+ doesn't
work this way; the host always starts the actual protocol processing
session via command to the remote, without user intervention.
B+ handles this as a two-step process. First the host issues an <ENQ>
char to the remote. When the remote "sees" an <ENQ> in the incomming
data, it calls the BPProtocol.HandleENQ method. This method responds with
a specific sequence of characters as a "support/sync handshake":
for Classic B: <DLE><'0'>
for QB: <DLE><'+'><DLE><'0'>
for B+: <DLE><'+'><'+'><DLE><'0'> (we use this)
This tells the host what level of B is supported by the remote and that
the remote's initial packet sequence number will be 0. It also initializes
several internal variables that are protocol-session dependent. Once the
remote has responded to the <ENQ>, the host may at any time (but not
nessessarily immediately or as the next char) send the remote the
two-character sequence <DLE><'B'>, the first two characters of a protocol
packet. The remote software's terminal handler must look for any <DLE>
received and call the protocol handler function bpDLESeen; the bpDLESeen
method verifies the <'B'> char and continues protocol processing or exits
as is appropriate.
This means that the protocol handler object needs to be instantiated
when you start your terminal handler loop (or before), so that the <ENQ>
handshake properly initializes the nessessary vars in the object for later
when the <DLE><'B'> sequence is received. For FTP and GIF uses, the
<DLE><'B'> will almost always immediately follow the <ENQ> response, but
for non-FTP purposes this may not be the case so we have to handle it that
way. Also, you can set your user options at the CIS host end so that an
<ENQ> is sent immediately after login (as a handshake) without starting
a protocol session, so the <ENQ> must be handled throughout the online
session.
Packet definition and layout
----------------------------
B Protocol "packets" are like XMODEM "blocks", but the term packet is
used because the actual data within may be self-standing and have no
connection to data in previous or subsequent packets. Each packet provides
the information nessessary for the packet handler to determine what to do
with the data, and even within a FTP or GIF session there may be packets
that do not contain file data. For example, in the CompuServe Information
Manager, when performing a FTP some packets are not file data but instead
is data used to update the "tracking" status line at the bottom of the
screen. During a protocol session, all commands and data in both
directions, other than packet ACKs/NAKs, are sent via packets.
The layout of a B packet:
<DLE><'B'><SeqNum><...quoted data...><ETX><quoted checkvalue>
The <DLE><'B'> identifies this as a B protocol block. The <SeqNum> field
is an ASCII digit 0 thru 9 (ASCII $30 to $39) incremented each packet and
wrapping back to 0 from 9. <...quoted data...> is the data block for this
packet, and contains any command info needed by the packet handler as well
as any "raw data" that might be needed in this packet. The data is quoted
as nessessary to protect volatile characters. <ETX> is the "end-of-packet"
flag character, and is sent unquoted. <Quoted checkvalue> may be either a
one-byte arithmetic checksum or two-byte CRC depending on negotiated
settings (see below), and is quoted as nessessary. Any given packet can be
a maximum of 1024 data bytes in size and can be less (as few as one byte).
The maximum size to expect is defined per handshake, and typically is
chosen based on current baud rate to provide a good balance between thruput
on good packets against turn-around time on flawed packets. CIS has shown
the best average has proven to be a maximum packet size that gives a packet
transmission time of 4 to 5 seconds at the current bps rate; at 300baud the
packet size should be 128 bytes, at 1200bps it should be 512 bytes, and at
2400bps and above it should be 1K bytes.
Quoting is performed by sending a <DLE> followed by the quoted data
byte. If the byte to be quoted in in the range $00..$1F, it is OR-ed with
$20 before sending, and if in the range $80..$9F it is OR-ed with $60.
When the receiving side sees a <DLE> in the packet other than in the
header, it assumes the next byte is quoted and reverses the sender's
quoting after discarding the <DLE>. The checksum/CRC calc is performed on
the byte's "natural" value, i.e. the CRC or checksum calc is performed on
the byte before quoting on the sending side and after the byte is un-quoted
on the receiving side. The <DLE> is discarded without inclusion in the
checksum/CRC calculation.
Classic B used a fixed quoting set, all chars in the range $00..$1F.
This proved to be excessive on most systems, increasing overhead
unnessessarily. In PC-based applications on most networks, only six
characters actually need to be quoted: <DLE>, <ENQ>, <ETX>, <XON>, <XOFF>
and <NAK>. The first three and <NAK> are used by the protocol itself and
must be protected to prevent the protocol from becomming confused, and the
flow control chars must be quoted to prevent unintended XOFF deadlocks.
Other platforms require different quoting sets, so B+ defines several
"standard" sets, but we only use 2: DQDefault (the minimum 6-char set) and
DQFull (the full set, equal to the Classic B set). For B+, DQFull is only
used during handshake, since at that point we have not negotiated the
quoting set and protocol level, and must assume the worst.
Each packet from either end must be acknowledged by the receiving end,
although such acknowledgements may be delayed so that multiple packets are
outstanding. Positive acknowledgements (ACKs) are sent as <DLE><SeqNum>
where <SeqNum> is the sequence number of the packet being ACKed. Negative
acknowledgements (NAKs) are sent as a single <NAK> character. Since the
NAK does not specifiy to which packet it applies, the packet handler must
keep track of how many packets are outstanding and assume the NAK refers to
the oldest. This also means that when a NAK is received, *all* outstanding
packets are discarded in order to keep the sequencing straight. Obviously,
the higher the number of outstanding packets, the greater the amount of
data that is resent - and the longer the "turn-around" time on a protocol
error. B+ is currently defined as having a maximum of 2 outstanding
packets. Tests by CIS have proved that more than two do not noticably
improve thruput while seriously degrading performance on error turn-around.
Packet types
------------
Although all packets follow the above format, packets have different
types. The first data byte of the packet (sometimes the first 2 bytes)
defines the packet's type and is not part of other data in the packet. For
FTP and GIF uses, the only types we are concerned with are "T", "N", "F"
and "+" packets.
"T" packets are transfer management packets. There are several subtypes
of this packet type, used for specific transfer management jobs. "N"
packets are "Next Data" packets, meaning they contain data that is a
continuation of the previous packet(s). "F" packets are Failure packets
and mean the protocol session is aborting for some reason. Again, there
are several possible "F" packet types depending on the reason for the
failure. The "+" packet is the special BPlus Parameters Handshake packet
used during protocol initialization.
"T" packets fall into the following categories:
"T" - Transfer Init. This packet contains the file type (Binary or
ASCII), the transfer direction relative to the remote (Upload
or Download), and the file's name for the remote's storage.
Sometimes called the "TD" packet.
"TI" - Transfer Information. This packet can contain several
different pieces of information (see the full B+ spec for a
detailed description of the fields in this packet). We
currently only use the FileSize field.
"TC" - Transfer Complete. Signals the end of the current file.
This packet will never contain file data; the last previous
"N" packet contained the end of the file or GIF data.
"Tr" - Resume Request. Special-case packet, sent by remote to
request the host attempt a resume. The "Tr" packet will
contain the size of the file (in bytes) that resides on the
remote's disk, and a CRC of the file calculated in the same
manner as a packet's CRC checkvalue. Note that CIS does not
currently support Upload Resume (where the CIS host resumes
a failed transfer of a file sent by the remote.)
"Tf" - Transfer Resume Failed. Special-case packet, sent when a
resume attempt request made by the remote failed. When the
remote requests a resume attempt it sends the length and CRC
of the "piece" of the file it has; the host verifies it's
local file is at least as long and has an equal CRC over the
matching length, and if either test fails the host sends this
packet.
The "N" packet is pretty much self-explanatory; it is a "Next Data
Block" packet and contains only actual file contents data. Since B packets
can be any length from one byte up to the maximum specified length, only
the exact file length is transmitted without padding.
"F" packets are typically sent for one of three reasons: the remote's
user requested the protocol be aborted via keystroke or other manual
method, the protocol has detected a hard failure such as too many
sequential error packets, or the remote experienced a file I/O error.
The host can issue Failure packets as well, and although they are rare we
must be prepared for them.
Typical Failure packet types:
"FA" - Failure/Aborted. Sent when the user requested an abort.
"FN" - Failure/Not known. Sent when an unsupported service was
requested by the host. CIS supports several file types
besides Binary and ASCII (for use on non-PC systems), but we
can't handle them so we send a FN if the file type is not
Binary or ASCII. Also, transfer direction flags other than
Upload and Download may be used on non-PC systems, so if we
see a direction flag other than "U" or "D" we send a FN
packet.
"FE" - Failure/Error. Usually sent when a hardware error occurs,
such as file I/O error.
Only the first two characters in a Failure packet have meaning to the
CIS hosts, the "F" and the failure-type flag. Traditionally a text string
is also sent in the packet as descriptive info on the failure, but this is
only for use by the remote if it chooses to display the reason string.
OOBPLUS follows this tradition.
The "+" packet type is a special case. It provides a method for the
host and remote to negotiate what level of B+ capabilities is to be
supported during this session and the quoting set to be used. The "+"
packet is typically sent between the <ENQ> handshake and the "T" packet.
The host sends its "+" packet first with its capabilities, and the remote
compares this information to its own capabilities and sends back a "+"
packet with what it can support. The usual "+" packet handshake will set
the number of outstanding packets that can be supported both on send and
receive, the type of checkvalue used (CRC or arithmetic checksum), the
maximum size of a packet in this session, whether the host will support
download and/or upload resume and whether the remote can support it, and
the quoting set to be used. See the interface section of OOBPLUS and the
ProcessTransportParams method of the BPProtocol object for more detail.
Flow of control
---------------
The overall flow of control of a B protocol session is basically the
same whether the transfer direction is Upload (from remote to host) or
Download (from host to remote). A diagram of a typical download is the
easiest way of visualizing the flow. An upload would be the same except
for the direction of the "N" packets and the possible resume request (no
resume on uploads). Notice that the host always starts the ball rolling.
(The following information is specific to FTP and GIF uses of the BPlus
protocol. Exact ordering may be different in non-FTP or GIF uses such as
HMI.)
HOST REMOTE
---- ------
<ENQ> sent -----------> Init vars,
Receive ENQ resp. <----------- Send ENQ Response
Set vars to match
... (possible non-protocol data flow between sides) ...
(NOTE 1)
Send "+" packet -----------> Compare host's options with ours,
set our options to "best-fit"
the host's settings
Configure to match <----------- Send "+" packet with our options
remote's capabilities
... (possible non-protocol data flow between sides) ...
(NOTE 2)
Send "T" packet -----------> Process "T" packet info, enter
protocol FTP mode, open file
{!! IF FILE DOES NOT EXIST, SKIP TO "JUMP HERE" !!}
(NOTE 3)
File exists: calc CRC on file,
build "Tr" packet with file's
size and CRC,
Gets "Tr" packet, <----------- send "Tr" packet
check size & CRC:
IF AND ONLY IF the
file size and/or CRC
checks fail, send
empty "Tf" packet -----------> "Tf" recd, rewind on file (either
overwrite from beginning, abort or
question the user what to do)
ELSE send first
"N" data packet,
contents of which
are assumed to
be appended to
the existing file -----------> append data to file, ACK packet
{JUMP HERE}
Send "N" packets -----------> write data to file, ACK packet
...(as many times as needed)...
Send "TC" packet -----------> Close file, flush pending ACKs,
exit FTP mode
...(back to normal terminal operations)...
Notes:
1) If the host is to send a GIF stream for decode/display, it will send the
"Start GIF Decode" sequence immediately before the "+" packet. It will
also send an <ENQ> after the start-decode sequence, so we must handle it.
2) If in GIF decode mode, there will be no "T" packet as it is unnessessary
(there is no filename, and the direction and data type are fixed at
Download and Binary). The first packet after the "+" packet will be the
first "N" packet of the GIF stream's data.
3) No resume is possible on a GIF stream; it always starts from the
beginning.
Performance, thruput and packet size
------------------------------------
When send-ahead is enabled (QB or B+ mode), multiple packets can be
outstanding (meaning no ACK or NAK has been processed for that packet).
The design of B+ is such that it always tracks the number of packets that
are outstanding rather than a specific offset within the file (ala ZModem)
or other criteria. This simplifies the tracking process, but it has the
negative effect of requiring all outstanding packets to be discarded and
resent when a packet is NAKed for some reason.
Ultimate thruput for any protocol is a function of several factors: the
port speed, percentage of overhead, turnaround delays on packet ACKs, and
turnaround delays caused by defective packets that must be resent. B+
essentially eliminates ACK turnaround delays, so raw thruput will closely
approach the raw port speed. On binary transfers, B+'s quoting overhead
will typically reduce data thruput to 92% - 96% of the raw thruput, while
ASCII transfers will seldom be less than 98% of raw thruput. However, if
one or more packets are defective and must be resent, the turnaround delay
for the defective packet will be several seconds because multiple packets
must be discarded and resent. Obviously this seriously degrades overall
thruput, although the degredation percentage is a function of the number of
packets in the transfer, the size of a packet and the number of "hits" that
cause turnaround. A substantial amount of testing has been performed by CIS
personnel on this subject, and it has been determined that a send-ahead
window of 2 packets gives the best thruput with minimal turnaround delay,
when used with a packet size that gives a per-packet transmission time of 4
to 5 seconds at the current port bps rate. Smaller packets improve
turnaround delay substantially, but hurt thruput on "clean" packets;
packets larger than 1024 bytes do not markedly improve clean thruput but
seriously hurt turnaround times. I have implemented packet size as a
function of port bps rate in the same manner and using the same criteria as
CIS software, but my own experimentation has shown that, at 2400bps, a
512-byte packet size reduces average data thruput by only 2% - 5% while
reducing turnaround delays 50%. A like reduction of default packet size at
1200bps produces similar results. For most users the "factory settings"
will be fine, but you may wish to make max packet size settable via
external configuration to provide greater flexability - for example,
providing a config option for "normal lines" or "noisy lines", and reduce
the max packet size setting if the "noisy lines" option is selected.
NOTE: current B+ implementations on CIS do not support re-negotiation of
transport parameters during FTP or GIF sessions once a given protocol
session has started, although it will "drop out" of send-ahead mode and
revert to "send and wait ACK" mode if it sees a high percentage of
defective blocks. The special version of B+ used by CIS hosts for HMI
does support such re-negotiation. Adding re-negotiation capability for
standard FTP and GIF sessions would be wonderful, but would break existing
implementations.
Other things
------------
Most developers will implement watching for <ENQ>, <DLE> and GIF Decode
Start signals from the host within some sort of terminal emulation code,
possibly based on Async Professional's OOEMU unit's ANSI emulator. CIS
supports ANSI/VT100, so this is convenient and works well. However, be
aware of one point: CIS hosts actually use VT100 terminal command
sequences. VT100 and DOS ANSI (ANSI.SYS and emulators built to mimmic it)
are both unequal and improper subsets of the full ANSI X3.64 terminal
control standard. Specifically, DOS ANSI handles defaults on the "J" and
"K" commands differently than VT100, and an emulator that exactly mimmics
DOS ANSI will find itself clearing the whole screen at the wrong times.
Be sure your emulator setup processes the "J" and "K" commands in the
VT100 manner when on CIS.
CIS also can support a subset of the VT52 sequences. The <ESC><'I'>
interrogation is a VT52 sequence, for example. You may wish to implement
VT52 processing as well as VT100 to eliminate potential misunderstood
commands. A child of Async Professional's AnsiEmulator can easily be
derived that handles both command sets within one emulator, and is
recommended.
When you log on to CIS, the node you call defaults to Even parity/7
data bits parameters. As mentioned above, a proper full B+ implementation
needs to know when to switch to N81 parameters for the actual protocol and
back once the protocol session is complete, but port and/or modem hardware
can cause problems with the switch. You are usually better off to force
N81 parameters for the entire online session. To do so you need to ignore
both parity and framing errors from the port handler, and to mask all
incomming non-protocol chars to 7 bits. CIS has a user configuration
option to select N81 parameters, but this won't become active until you
have logged in to CIS, so be prepared to cope. The TERMBP example program
demonstrates one method of handling this.
CIS supports other networks besides its own. Such "suplimental
carriers" often behave differently than CIS' own network. Tymnet in
particular seems to have occasional problems, though these are usually
specific to the node you call. Specifically, Tymnet uses a PAD (Packet
Assembler/Disassembler) system that uses the <DLE> char as a signal
itself. In order for CIS to use the <DLE> char for its own purposes, it
must tell the Tymnet network to consider that char "transparent". CIS
issues the nessessary command automatically, but some Tymnet nodes are
slow to update themselves. In this case the <DLE> is seen as a PAD
command and is "swallowed" by the Tymnet node. Tymnet then sees the <'B'>
as an unknown command and issues a "pad command error" string to the
remote. Meanwhile CIS thinks it has switched things and has sent the "+"
packet, and is waiting for a response. A long timeout ensues. This
doesn't seem to crop up too often on normal FTP transfers, but is not
uncommon on GIF sends. Thankfully this problem only seems to occur on a
few nodes.
---- GIF Information --------------------------------------------------------
First off, if you intend to implement GIF decoders, either off- or on-
line, you absolutely need the GIF 89a spec document. It can be obtained on
CompuServe (see the top of this file for info.) OOBPLUS is primarily a B
protocols unit, with GIF decoding technology provided as demonstration
only; trying to go into detail on GIF itself and the internals of a GIF
decoder is really outside the bounds of this document. However, the GIF
spec documents provide very little information on actual online decoder
implementation details other than some general rules. I'll try to fill
those gaps here.
GIF (Graphics Interchange Format (sm)) is a definition for transmission
and display of raster-based graphics image data - in other words,
"bit-mapped" graphics. GIF supports images of up to 2048 by 2048 pixels
and up to 256 colors. Extensions allow support for "full-color" images
such as 24-bit Targa. GIF provides services for anamation and text display
as well. GIF was developed by CompuServe as a medium for online graphics,
with an eye towards future development in online graphics environments (ala
Prodigy.) GIF in combination with "Transport Layer" B+ protocol provides a
rich platform for this type of environment. GIF rapidly gained acceptance
in the file-based imaging world as well, because of its space efficiency,
ease of processing and device independence - so rapidly, in fact, that
online GIF development has been practically non-existant. CIS early on
set up a "Developer's Group" of graphics specialists and programmers to
help steer the directions of GIF, but only myself and one other member of
that group worked primarily with the online development aspects of the
spec (and that other member has pretty much ceased online development.)
Not surprizingly, only myself and one or two other members of that group
develop primarily in Turbo Pascal; C and assembler are the norm.
My primary purpose in providing both the GIF children to the B+ objects
and the example decoders is to spark interest in GIF development,
especially online. The horizons opened up by realtime online graphics are
huge; the surface has only been barely scratched. I am deluged with calls
from other developers and even more from companies and individuals with
some idea for online graphics usage, wanting me to write this or that
program (even whole BBS systems with built-in GIF databases!); I've taken a
couple of these contracts but there's many, many more where they came from.
(Tip: the real estate industry especially seems to be hurting for a good
BBS-type system for Boards Of Realtors or Brokers to run for their agents,
that provides a database of listings with images made from pictures of the
properties. I get more requests in this area than any other. I'm not sure
why but Medical Imaging is another popular area, although GIF's standard
color resolution is generally considered insufficent for many medical
imaging needs.)
GIF general info
----------------
GIF is a stream format. Besides making it simple to implement online
display, this means GIF image data may formally be included in other data
or that multiple GIF streams may easily be made into a "graphics database".
CIS has steadfastly refused to formally define a "file format" for GIF, but
a format for file storage has evolved over the years and is adhered to by
all known decoders whose primary purpose is display of GIF files. As long
as the first 6 bytes of the file's contents are the GIF signature
("GIF87a" or "GIF89a"), the image data is contiguous and a valid
terminator exists, it can be handled by most any offline decoder.
CIS maintains a "storage class" flag for each file in its forum
libraries. If you BROwse a library's directory, you will see a "/bin" or
"/gif" or "/rle" next to the filename on many entries. When uploading a
file to CIS, you are prompted for, among other things, the file's type.
Most users assume that "binary" and "ASCII" are all there is, but GIF image
files should be uploaded with a file type of "GIF". Even if a file
contains a GIF image and has filename extension of ".GIF", CIS will not
allow online display of the file's contents unless the file's storage class
is GIF as well.
GIF image data, with the exception of the header blocks, is binary
raster-based sequential pixel color data, compressed using a modified form
of the LZW compression method. The image raster data is uncompressed on
the fly as it is being displayed. It is somewhat difficult for the LZW
decompressor to itself determine when a invalid byte is received - usually
it is not detected until it has corrupted the codes table in the decom-
pressor, often leading to a lockup. Most decompressors do no or only
minimal error-checking; they assume the data handed to them is valid.
For this reason the GIF spec notes that the image data stream must be
guaranteed error-free. For disk-based streams this is assumed, but for
online decode the stream must be protected by protocol. The GIF spec does
not define one particular protocol that is recommended for image streams.
This is important for the bigger world of Bulletin Board Systems; it means
that it is perfectly acceptable to implement an online GIF decoder that
uses ZModem or some other protocol, so that users of BBS' without CIS B+
support can still easily view GIF images in an online environment. Since
the main thrust of this unit is the B+ protocol I am only detailing
decoding GIF streams from B+ encapsulation, but careful study of the
example code in OLGIF.PAS should make it fairly obvious how to implement a
GIF decoder using the other protocols Async Professional provides.
The GIF spec does define the two protocols CIS computers will use for
transmission of online GIF display streams: raw stream data (assumed to be
protected by a link-level protocol such as MNP or V.42, and not to be used
without such link-level protection) and B+ protocol. It also defines a
"handshake" system to be used to determine both that GIF is supported for
online display, and what level of protocol and hardware support is
available on the particular remote. While only CIS uses this handshake
system, it behooves developers to support it for online decoders based on
other protocols as well, as a standard needs to evolve on this particular
point.
The handshake consists of 3 different "private" ANSI command sequences
("private" because only CIS defines them). These use the standard ANSI
X3.64 introducer sequence (<ESC><'['>) followed by a right pointer bracket,
a numeric code specifier and the command type tailer character. The
sequences are:
<ESC>[>0g - sent by host to interrogate remote for GIF/protocol/hardware
support. If the remote supports GIF display, it returns a
string consisting of codes that tell the host to use raw
stream or B+ transmission, what screen resolutions (X by Y,
and number and depth of colors), whether hardcopy output is
supported, and which version of the spec the decoder supports
(87a or 89a). See the spec document for the various details
of this code scheme.
<ESC>[>1g - begin online decoding to the local screen. This will be sent
by the host immediately before the <ENQ> to handshake B+
support. Once this is received, you can safely assume that
the next <ENQ> will be to handshake the start of a B+ session
to transmit the GIF stream. Non-B+ sessions will see the
next char after the command be the first char of the GIF
signature starting the image data.
<ESC>[>2g - begin decoding to the local hardcopy device. Lots of systems
still in use simply do not have the video capabilities to
adequately display GIFs, especially the higher-resolution/
high number of colors images available from many sources.
Also, some areas of the service provide graphics that are of
more use as hardcopy than on the screen (such as the weather
maps, or the stock performance graphs in the TRENDS area).
I don't support hardcopy decode in this set of examples, but
it is fairly easy to implement and a handy capability. The
handhake sequence above can be used to tell the host we want
hardcopy decode, and for color printers can tell the host
to use provide normal color information.
One important note concerning the "0" code above: it is *not* sent every
time an online GIF decode is requested. In the forum areas of CIS, it is
usually sent the first time you request a online decode and not sent again
until you leave that forum, either to another area of CIS or back to the
same forum. Most of the non-forum areas of CIS that support GIF at all
make GIF support their primary purpose, such as the weather maps areas and
TREND; these areas will send the "0" inquiry when you first enter the area
before they display any menu text or prompts. Your program must be able to
handle the "0" inquiry at any time, just as it must be able to process a B+
<ENQ> or <DLE> at any time.
Until recently there were a few areas of CIS that provided GIF support
but did not support B+ encapsulation (the TREND area was the most commonly
accessed.) These areas defaulted to raw stream even if the remote reported
its ability to handle B+, so a decoder had to be able to determine if B+
was initializing or not and process incomming data accordingly. As you can
imagine, this lead to a lot of headaches, and problems for users when the
unprotected stream took a line hit and locked up the system. To the best
of my knowledge there are no such areas left on CIS; all areas that provide
any support for online GIF decoding support B+ encapsulation, and all areas
support B+ by default unless specifically told otherwise by the "0" code
response string described above.
The flow of control for B+ when processing a GIF stream for display is
very little different from a "normal" FTP session, with a couple of notable
exceptions:
1) As mentioned in the B+ infomation above, there is no "T" packet sent
during a GIF display. Since many of CIS' GIF streams are built and sent
"on the fly" in real time they have no filename, and obviously the
transfer's direction and data type are fixed at "Download" and "Binary".
This only complicates matters a little bit, and only if you are writing the
image data to a file as well as displaying it (something every online
decoder should do, but that CompuServe's own software products do not.)
If you do write the image to a file, it's your responsibility to determine
a name for the file and manage getting the stream data into the file; see
OLGIF.PAS and TERMBP.PAS for an example. Such processing really should be
part of the BPProtoGIF object, but I have not included it that way because
the spec does not define the capability; you may wish to derive a child of
BPProtoGIF that does.
2) The data in each "N" packet is yours to do with as you wish. The
BPProtoGIF object provides a method (bpGetGIFDataBlock) that gets the next
pending "N" packet and hands you the data within it; what you do with it
from there is your business. This provides an example of possibilities for
non-FTP uses of B+, but it also points out that protocol management is to a
large extent outside the bounds of the BPProtoGIF object - if you don't
call bpGetGIFDataBlock, or fail to call it in a timely fashion, the
protocol will time out and abort. Also, to give the GIF decoder enough
power to properly manage the protocol flow, packets must be ACK-ed by the
GIF decoder itself (defective packets are handled automatically within the
GetPacket method of the BPProtocol parent of BPProtoGIF). The same goes
for sending "F" packets in case the user wants to abort the decode or the
decoder itself has a problem. With power comes responsibility; study the
OLGIF.PAS file for an example of how to manage the flow control properly.
OOBPLUS and GIF
---------------
GIF data may come from a single file, the serial port, as a piece of a
file from database-type storage, etc., and may be sent to the screen, a
printer, or some other output device, so it makes sense to isolate the
process of getting the stream's raw data and outputting the processed image
data from the decoder's inner workings. Doing so also makes implementing a
GIF decoder thru a protocol much easier. The demo decoders' kernel
(DEGIF.PAS) names two procedure pointers, one to a function which returns
the next byte of raw data and one that does something with a processed
raster line of display data, and refers only to these proc pointers for
source and destination. Various "shells" can then be wrapped around the
decoder to implement the routines for a particular decoder's needs.
This allows any protocol that gathers data in packets or blocks to hand
a block of data to the decoder shell and go on about its business; the
shell will dole out the bytes to the decoder. The downside (at least for
single-thread operating systems like DOS) is that actual decode/display
only takes place between packets, which gives the decode the appearence of
occuring in spurts. It also makes basing a GIF decoder on true streaming
protocols like ZModem a bit trickier, as the decoder has to get some time
to do its job.
The BPProtoGIF object provides two methods for the decoder. The first,
the bpDLESeen virtual method, handles the "+" packet reception and
processing. It is a function, returning True if it is successful and False
otherwise; the decoder should abort if the function returns False. The
second is the bpGetGIFDataBlock function, which returns True and a packet's
worth of raw GIF stream data if successful, False if the packet reception
failed for some reason. bpGetGIFDataBlock also returns a Boolean var
parameter showing whether the "TC" packet has been received, to make it
easier to see the end of the protocol session.
When the main program sees the GIF decode startup ANSI sequence, it
should initialize itself and start looking for an <ENQ> from the port to
handshake the B+ protocol session. Immediately after the <ENQ> should be
the <DLE> starting the host's "+" packet; the decoder calls bpDLESeen at
this point. From here until we receive the "TC" terminating packet, all
port data will be B+ encapsulated GIF image data, to be processed by the
decoder. We simply loop calling bpGetGIFDataPaket and handing the received
bytes to the decoder until done. Review OLGIF for examples.
Video support considerations
----------------------------
There is a multitude of video hardware types available for PCs. How do
we support 'em all? Which should we support?
In today's hardware market, SVGA cards are the most common video cards
sold. SVGA support is a "must" in a GIF decoder. But... Any graphics
programmer will tell you: programming for SVGA can be a royal nightmare.
At least a dozen "common" main SVGA chipsets (more all the time) and, once
past the "IBM-standard" EGA/VGA modes, each having different mode
resolutions, different mode select numbers for those resolutions that are
the same, and different coding requirements to manage video memory
bankswitching and plotting.
The TP BGI's provide a high level of device independence, and can be a
reasonable starting point for a GIF decoder. But GIF's device-independent
nature doesn't keep device dependence from cropping up. Because GIF
encoders and decoders are implemented on a wide variety of platforms, many
of which have video resolutions different from the common PC video modes, a
sizable percentage of GIF images are not "sized" to fit well on a PC screen
unless special handling is performed. Also, since GIF is a high-resolution
format, it is commonly used with video hardware for which there is no BGI
support (including Targa, TIGA, IBM's new HGA and others). Only a few
SVGA BGI drivers are available, and do not cover all common SVGA chipsets.
While decoder speed is not much of an issue in online environments until
the port bps rate reaches 9600 and above, a production offline decoder
that paints an image on the screen at BGI speeds will be considered
totally unacceptable. Finally, the BGI's are pretty topheavy considering
that GIF support only requires 2 video routines to do its job - a method
of setting the video card to the needed graphics mode and back to text
mode when done (a simple video BIOS call), and a routine to plot pixels or
raster lines on the screen. An intelligently written set of routines done
in assembler that will detect and support all common video types from Herc
mono to SVGA, complete with SVGA chipset autodetect code and bankswitching
code, will take less code and data space than a single BGI driver linked
into the program. Turbo Pascal 6.0's BASM makes an ideal environment for
the nessessary assembler, though I have stuck with straight TP in the demo
code for the sake of compatibility with earlier versions of TP and to be
more informative. Although it can be done, it is no trivial job to write
straight TP code that directly manipulated SVGA hardware since most
chipsets require substantial diddling of ports; VGA and lower video types
can be dealt with in straight TP code quite acceptably, but assembler is
still preferred on these cards for best speed.
The easy way out is to use the BIOS services for pixel plotting. This
is only marginally faster than the BGI's, but easier to deal with and with
substantially less code overhead. It is a dangerous way to go, though,
especially on CGA and earlier EGA and VGA cards - these older cards'
BIOS routines for plotting typically disable interrupts while plotting the
pixel, which in an online environment leads to dropped characters at the
port and a high percentage of resent data. And on Hercules monochrome cards
it is unavailable. I have mixed the best of both worlds in the demo
decoders (see GIFVIDEO.PAS), but a production program should use direct
hardware manipulation as far as possible for best speed and minimum
potential interference with the serial port.
That said, there's a cure on the horizon: VESA. VESA is a set of
standards for SVGA detection, mode selection and video manipulation that
provides a consistent set of mode resolutions, commands and data access to
eliminate most all the confusion between SVGA chipsets. VESA is still new
but gaining support by leaps and bounds; all the major video manufacturers
support the VESA standards at least via a TSR. Some are now bringing to
market cards that support VESA standards "hardwired" on the card,
although this is currently quite difficult due to the severely limited
room in most SVGA video BIOS ROM chips. There are no VESA-compliant SVGA
BGIs available that I know of, but at least one is currently under
development and looks very good. In this most recent version I have added
the nessessary VESA support to work with this code; it is in no way a
complete VESA interface, but it does work on all cards/drivers I have
been able to test with.
On the subject of CGA and Hercules mono support: I have included
support for these hardware types, but really only for their instructive
capability. CGA and Herc mono simply do not have the size and/or color
resolution to do justice to the vast majority of GIF images today unless
all sorts of sneaky tricks are used. With CGA and Herc mono rapidly
becomming a thing of the past (a low-end VGA card and mono analog monitor
costs about the same as a Herc card and mono TTL monitor and a *lot*
less than CGA), there's even less reason to expend effort and code space
on their support. However, if you need such support, you will need to
implement some sort of dithering method to do color reduction. In GIF
context, "dithering" an image means to perform colors reduction so that
the colors in the original image are mapped to best fit the available
palette on the local system. There are a number of common color-reduction
dithers in use. Most all work on the principle of error distribution;
spreading out the color error over surrounding pixels. Just about all of
them require the decoder to have access to pixel data "in front of" the
current pixel - data that is unavailable to a decoder getting its data
from the serial port unless relatively complex buffer management is used.
Implementing such dithers is left to your skills and imagination, with the
following tip: there is a large base of dither technology available in
LIBrary 14 of the PICS forum on CIS, although practically none of it is in
Turbo Pascal. If you can wade thru C and assembler code, you'll find lots
of good information with which to get started. A good dither method to
reduce 256-color maps to 16 colors is important for EGA support as well.
The switch from text mode to graphics mode is a sticky spot for online
GIF. The header blocks must first be received so the decoder can
determine which mode to use before making the switch, so data will be
typically be streaming into the port when it is time to make the switch.
Most every video BIOS in captivity disables interrupts while it switches
modes, so at least a few bytes will be dropped by the port - causing a
defective packet and a resend. This doesn't cause the protocol any grief
it can't handle, but it does slow things down unnessessarily. The cure is
relatively simple: send the host an XOFF and wait for the port to become
"quiet" for some arbitrary length of time before making the mode switch.
Async Professional does not provide a method for waiting for a port to
quiese, so the demo adds a method for doing it; the method employs a sneaky
trick and goes against good OOP programming, so you may want to implement
such a method within the port handler object. Don't forget to send an XON
after the mode switch is complete!
---- Beyond what's here: where to get help ----------------------------------
The demo decoders are fully compliant with the 87a version of the GIF
spec. They will also decode GIF streams built to the 89a version of the
spec, but will ignore all 89a extensions. The DEGIF.OBJ module is
copyright (c)1989 Cyborg Software Systems, Inc., 3119 Cossell Drive,
Indianapolis, IN 46224 USA., and used by permission of its author.
As mentioned before, dealing with the vagaries of SVGA hardware can be
a daunting task. John Bridges, author of GRASP and PCPaint, is a past
master at it. John periodically uploads a file to LIBrary 14 of the PICS
forum called VGAKIT.ZIP, that contains SVGA chipset autodetect and
bankswitch code, example plotting code, code to set and use special "not
supported by BIOS" modes on the VGA, and other juicy tidbits. Be prepared
for MASM code written primarily to interface to MSC, but the conversion to
TASM and TP is fairly straightforward; John and I spent some time last
year working the kinks out so his MASM code requires minimal changes to
port to TP/TASM.
The best allaround source for GIF help is the Developer's Den section
(17) of the PICS forum. The place is haunted by some of the best and
brightest graphics minds in the world, all ready and willing to share their
expertise. You'll find specialists in ray-tracing, format conversion,
fractals, high-end video, scanning, etc.
I can be contacted via CIS at User ID# 70007,3574. I frequent several
forums at least once a day, especially IBMCOM, PCVENB (TurboPower's section
6), BPROGA and PICS. The TurboPower folks will be happy to help you as
well, but to help keep their support headaches in check I hope you'll
contact me first; they may need to forward questions to me anyway.
US Mail will reach me at:
Ozarks West Software
14150 Gleneagle Dr.
Colorado Springs, CO 80921
{==== Reference Section ======================================================}
The interface section of the OOBPLUS unit is provided below.
{-----------------------------------------------------------------------------}
{The following define is used to specify status updates at more frequent
intervals than normal. This makes the status display more informative and
causes the "Time to go" field to just about tick like a clock on downloads,
but it can have a negative effect on thruput at higher bps rates and can
cause the CPS field to "jitter" somewhat, especially on uploads.}
{$DEFINE ShowRates}
{The following define enables support of online GIF decoding. If you are not
going to use GIF support, un-defining this can both save about 400 bytes of
code space and allow you to use the unit even if you don't have TurboPower's
Object Professional or Turbo Professional libraries. The provided example
program TERMBP.PAS does require Object Professional to compile.}
{$DEFINE SupportGIF}
{$IFNDEF UseOpro}
{$IFDEF SupportGIF}
!!! The options selected are incompatible with this unit !!!
{$ENDIF}
{$ENDIF}
interface
uses
DOS,
{$IFDEF UseOpro}
OpCrt,
{$ELSE}
{$IFDEF UseTpro}
TpCrt,
{$ELSE}
Crt,
{$ENDIF}
{$ENDIF}
ApMisc,
ApTimer,
ApPort,
ApUart,
OOCom,
OOAbsPcl;
const
UnitVers = '1.2f';
UnitDate = '24-Apr-91';
{consts needed here for status, continued from ApMisc}
ecResync = 9980;
ecWaitACK = 9981;
ecDropout = 9982;
ecHostCan = 9983;
ecFileIO = 9984;
ecTryResume = 9985;
ecHostResume= 9986;
ecResumeOK = 9987;
ecResumeBad = 9988;
ecOverwrite = 9989;
ecUnPacket = 9990;
BP_Timeout_Max = 30; {max allowed timeout per-char}
BP_Error_Max = 10; {max sequential errors}
BP_Buffer_Max = 1032; {largest data block available}
BP_Abort_Max = 3; {number of abort requests req'd to trigger Override}
BP_SendAhead_Max = 2; {max number of packets we can send ahead}
{minimum <ESC><'I'> (and GIF support interrogation) response strings}
{$IFDEF SupportGIF}
ESCI_Response : String[80] = '#IB1,SSxx,GF,PB,DT';
{see the GIF87a or 89a spec for explanation of these codes}
GIFReplyEGA = '#87a;1;0,320,200,4,0;0,640,200,2,2;0,640,350,4,2';
GIFReplyCGA = '#87a;1;0,320,200,2,0;0,640,200,1,0';
GIFReplyHGC = '#87a;1;0,720,350,1,0';
GIFReply : String[60] = GIFReplyEGA;
{$ELSE}
ESCI_Response : String[80] = '#IB1,SSxx,PB,DT';
{$ENDIF}
type
{used by GetResumeProc for resume request handling}
ResumeResultType = (xfrResume, xfrOverwrite, xfrRename, xfrAbort);
BufferType = Array[0..BP_Buffer_Max] of Byte; {a buffer of data}
SABuffType = {windowing buffer:}
record
Seq : Integer; {this sequence number}
Num : Integer; {this packet's data size}
Buf : BufferType; {this packet's data}
end;
SPackets = Array[0..BP_SendAhead_Max] of SABuffType;
QuoteArray = Array[0..7] of Byte; {for quoting params sets}
const
DQFull : {all chars in ranges $00..$1F and $80..$9F}
QuoteArray = ($FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF);
DQDefault : {ETX ENQ DLE XON XOFF NAK}
QuoteArray = ($14, $00, $D4, $00, $00, $00, $00, $00);
DQExtend : {same as DQDefault plus XON & XOFF w/ high bit set}
QuoteArray = ($14, $00, $D4, $00, $00, $00, $50, $00);
DQClassic : {Classic B set, all chars in range $00..$1F}
QuoteArray = ($FF, $FF, $FF, $FF, $00, $00, $00, $00);
type
ParamsRecord = {xfer params record:}
record
WinSend, {send window size}
WinRecv, {recv window size}
BlkSize, {block size (* 128)}
ChkType : Byte; {check type, chksum or CRC}
QuoteSet : QuoteArray; {chars to quote}
DROpt, {DL Recovery option}
UROpt, {UL Recovery option}
FIOpt : Byte; {File Info option}
end;
{protocol direction options}
DirectionType = (Upload, Download);
BPProtocolPtr = ^BPProtocol;
GetResumeProc = function(BPP : BPProtocolPtr) : ResumeResultType;
ChkAbortProc = function : Boolean;
BPProtocol = {abstract BPlus object:}
object(AbstractProtocol)
Ch : Integer; {curr char sent/recd}
Quoted : Boolean; {true if last ch recd was quoted}
QuoteTable: Array[0..255] of Byte; {our active quoting table}
Checksum : Word; {may hold CRC}
Direction : DirectionType; {upload or download}
DefResume : ResumeResultType; {default resume handling}
GetResume : GetResumeProc; {determine how to handle resume}
ChkAbort : ChkAbortProc; {see if user wants an abort}
HisParams : ParamsRecord; {host's parameters}
OurParams : ParamsRecord; {our parameters}
AbortCount: Integer; {# of abort requests so far}
ResumeFlag: Boolean; {true if resuming an aborted dl}
ResumeOK : Boolean; {true if resume was successful}
Aborting : Boolean; {true if processing abort}
FatalAbort: Boolean; {true if OverrideAbort}
ShowStatus: Boolean; {False only for GIF}
PacketRecd: Boolean; {true if packet recd in SendPacket}
BPlus : Boolean; {true if in full B+ mode}
ClassicB : Boolean; {true for "original" B proto}
UseSQuote : Boolean; {true if using special quote set}
SQuoteSet : QuoteArray; {user's specified quote set}
RSize : Integer; {size of last recd buffer}
BuffeRSize: Integer; {current allowed recv size}
RBuffer : BufferType; {receive buffer}
SBuffer : SPackets; {sending buffers}
SeqNum : Integer; {current sequence number}
Next2ACK : Integer; {packet pending ACK}
Next2Fill : Integer; {packet to load for send}
SAMax : Integer; {highest current sendahead cnt}
SAWaiting : Integer; {# of packets outstanding ACKs}
SAErrors : Integer; {keep track of SendAhead errors}
R_Raw : LongInt; {vars for status display}
R_Packets : LongInt;
S_Raw : LongInt;
S_Packets : LongInt;
{$IFDEF ShowRates}
R_Counter : LongInt;
S_Counter : LongInt;
{$ENDIF}
constructor Init(AP : AbstractPortPtr);
destructor Done; virtual;
{...public methods called by terminal handlers}
procedure bpHandleENQ;
{-handle an <ENQ> from host}
procedure bpHandleESCI;
{-handle <ESC><'I'> (VT52 terminal capabilities inquiry) from host}
function bpDLESeen : Boolean; virtual;
{-called when <DLE> seen from host, starts protocol}
{...other publics}
procedure bpSetResumeProc(RP : GetResumeProc);
{-set our ResumeProc for this instance}
procedure bpSetChkAbortProc(CAP : ChkAbortProc);
{-set our ChkAbortProc for this instance}
procedure bpSendACK;
{-acknowledge receipt of a complete packet}
{...private methods}
procedure UpdateStatus(W : Word);
procedure UpdateQuoteTable(QS : QuoteArray);
procedure QuoteThis(Value : Integer);
procedure apResetProtocol; virtual;
procedure apUpdateBlockCheck(CurByte : Byte); virtual;
function CheckAbort : Boolean;
procedure SendByte(C : Char);
procedure SendQuotedByte(I : Integer);
procedure SendNAK;
procedure SendENQ;
function IncSequence(Value : Integer) : Integer;
function ReadByte : Boolean;
function ReadQuotedByte : Boolean;
procedure SendFailure(Reason : String);
function ReadPacket(LeadInSeen, FromSend : Boolean) : Boolean;
procedure SendData(BNum : Integer);
function IncSA(Value : Integer) : Integer;
function ReSync : Integer;
function GetACK : Boolean;
function SAFlush : Boolean;
function SendPacket(Size : Integer) : Boolean;
function SendTransport : Boolean;
procedure ProcessTransportParams(SendXPortInfo : Boolean);
procedure bpInitVars;
end;
BPProtoFTPPtr = ^BPProtoFTP;
BPProtoFTP =
object(BPProtocol)
constructor Init(AP : AbstractPortPtr;
DefaultResume : ResumeResultType);
destructor Done; virtual;
{...public methods}
function bpDLESeen : Boolean; virtual;
{...virtual methods}
procedure apPrepareReading; virtual;
procedure apFinishReading; virtual;
procedure apFinishWriting; virtual;
procedure apPrepareWriting; virtual;
{...private methods}
procedure SendFile;
procedure RecvFile;
end;
{$IFDEF SupportGIF}
BPProtoGIFPtr = ^BPProtoGIF;
BPProtoGIF =
object(BPProtocol)
constructor Init(AP : AbstractPortPtr);
destructor Done; virtual;
{...Public methods}
function bpDLESeen : Boolean; virtual;
function bpGetGIFDataBlock(var P;
var PSize : Word;
var IsLast : Boolean) : Boolean;
end;
{$ENDIF}
function bpStatusStr(Code : Word) : String;
procedure NoUserStatus(AP : AbstractProtocolPtr; First, Last : Boolean);
{===== Procedure/Function/Method Reference ===================================}
This section provides details on the public and virtual methods of the
various BPlus protocol objects.
{----- BPProtocol object -----------------------------------------------------}
constructor Init(AP : AbstractPortPtr);
Purpose:
Initialize BPlus protocol instance, including all shared vars from
AbstractProtocol and those BPProtocol vars that are instance-dependent.
Like other protocol objects derived from AbstractProtocol, if the constructor
fails the reason code is returned in the AsyncStatus global.
Example:
{...}
Var
OurBPlus : BPProtocol;
{...}
if NOT OurBPlus.Init(OurPortPtr) then
WriteLn('Error ',AsyncStatus,' initializing BPlus protocol object!');
{...}
destructor Done; virtual;
Purpose:
Dispose of BPProtocol instance when processing is complete.
procedure bpHandleENQ;
Purpose:
Perform protocol-dependent processing when a <ENQ> char (ASCII $05) is
received at the serial port. This method initializes some internal vars
and sends the proper response back to the CIS host.
Example:
{...}
case RecdChar of
#5:
OurBPlus.bpHandleENQ;
else
Write(RecdChar);
{...}
See also:
bpHandleESCI, bpDLESeen
procedure bpHandleESCI;
Purpose:
Handle responding to <ESC><'I'> inquiry from CIS host. See the detailed
description in the protocol spec document for more information on the
codes that are used to build the response string.
Example:
{...}
if (LastRecdChar = #27) and (RecdChar = 'I') then
OurBPlus.bpHandleESCI;
{...}
See also:
bpHandleENQ, bpDLESeen
function bpDLESeen : Boolean; virtual;
Purpose:
Called when a <DLE> char (ASCII $10) is received from the CIS host. This
function calls the main protocol processing loop. It returns True if
protocol processing completed successfully, or False if a error occurs
during protocol handshaking or if the protocol session is aborted for any
reason. Note that in the BBProtocol object this is only an abstract method.
Example:
{...}
if (RecdChar = cDLE) then
if OurBPlus.bpDLESeen then ;
{...}
WRONG! In the BPProtocol base object, bpDLESeen is an ABSTRACT method that
will generate a runtime error (211) if called directly.
See also:
bpHandleENQ, bpHandleESCI
procedure bpSetResumeProc(RP : GetResumeProc);
Purpose:
Assign a function to the BPProtocol object's GetResume proc pointer to tell
the protocol which type of handling should be used for resuming an aborted
download. When a download is started and the protocol determines that a
file of the same name as the file being downloaded already exists, it can
proceed in one of several ways: it can abort in order not to risk
overwriting the existing file, it can overwrite the existing file
regardless, it can rename the new file being downloaded so as not to
conflict, or it can attempt a resume of a previous download. No one choice
is appropriate for all occasions, so this hook is provided for an outside
function to tell the protocol how to proceed. The function must be
compiled using the FAR model and cannot be nested within another procedure
or function. It takes the following form:
function GetResumeFunc(BPP : BPProtocolPtr) : ResumeResultType;
Typically this function prompts the user what to do and returns his/her
choice. See the definition of the ResumeResultType above.
Example:
{...}
OurBPlus.Init(OurPortPtr);
OurBPlus.bpSetResumeProc(GetResumeFunc);
{...}
See also:
bpSetChkAbortProc, and the type declaration of ResumeResultType above.
procedure bpSetChkAbortProc(CAP : ChkAbortProc);
Purpose:
Assign a function to determine if the user wishes to abort the protocol
session. Typically this will be set to point to a function that returns
True of the user has requested an abort (by pressing a key, clicking the
mouse on a hotspot, etc.) or False otherwise. Like bpSetResumeProc, the
function passed to this procedure must be compiled under the FAR model and
not be nested within any other procedure or function. The function takes
the form:
function ChkAbortFunc : Boolean;
Example:
{...}
OurBPlus.Init(OurPortPtr);
OurBPlus.bpSetChkAbortProc(ChkAbortFunc);
{...}
See also:
bpSetResumeProc
{----- BPProtoFTP object -----------------------------------------------------}
constructor Init(AP : AbstractPortPtr; DefaultResume : ResumeResultType);
Purpose:
Initialize BPProtoFTP object. The DefaultResume parameter provides the
default handling type for resume attempts in case no GetResumeProc has
been assigned, so totally automated systems such as "CIS navigators" can
continue processing without user intervention.
Example:
{...}
var OurBPFTP : BPProtoFTP;
{...}
OurBPFTP.Init(OurPortPtr,xfrOverWrite);
{...}
See also:
AbstractProtocol.InitCustom, BPProtocol.Init
destructor Done; virtual;
Purpose:
Dispose of BPProtoFTP instance when processing is complete.
procedure apPrepareReading; virtual;
Purpose:
Prepare for transmitting data via the protocol (usually opens the file to
be uploaded).
See also:
The other AbstractProtocol child objects' apPrepareReading methods.
procedure apFinishReading; virtual;
Purpose:
Perform any nessessary cleanup after the upload is finished (usually closes
the file uploaded).
See also:
The other AbstractProtocol child objects' apFinishReading methods.
procedure apPrepareWriting; virtual;
Purpose:
Prepare for receiving data via the protocol (usually opens the file to
be downloaded). In this instance the method also checks if a resume
attempt is needed.
See also:
The other AbstractProtocol child objects' apPrepareWriting methods.
procedure apFinishWriting; virtual;
Purpose:
Perform any nessessary cleanup after the download is finished (usually
closes the received file).
See also:
The other AbstractProtocol child objects' apFinishWriting methods.
function bpDLESeen : Boolean; virtual;
Purpose:
Called when a <DLE> char (ASCII $10) is received from the CIS host. This
function calls the main protocol processing loop. It returns True if
protocol processing completed successfully, or False if a error occurs
during protocol handshaking or if the protocol session is aborted for any
reason. In this BPProtoFTP instance, bpDLESeen either receives the "+"
packet and initializes the nessessary internal variables, or receives the
initial "TD" packet, verifies that all provided parameters are correct, and
sends or receives the file named in the "TD" packet.
Example:
{...}
if (RecdChar = cDLE) then
if OurBPFTP.bpDLESeen then ;
{...}
See also:
BPProtocol.bpDLESeen
{----- BPProtoGIF object -----------------------------------------------------}
constructor Init(AP : AbstractPortPtr);
Purpose:
Initialize BPProtoGIF object.
Example:
{...}
var OurBPGIF : BPProtoGIF;
{...}
OurBPGIF.Init(OurPortPtr);
{...}
See also:
AbstractProtocol.InitCustom, BPProtocol.Init
destructor Done; virtual;
Purpose:
Dispose of BPProtoGIF instance when processing is complete.
function bpDLESeen : Boolean; virtual;
Purpose:
Called when a <DLE> char (ASCII $10) is received from the CIS host. This
function calls the main protocol processing loop. It returns True if
protocol processing completed successfully, or False if a error occurs
during protocol handshaking or if the protocol session is aborted for any
reason. In this BPProtoGIF instance, bpDLESeen receives the "+" packet and
initializes the nessessary internal variables; all other protocol
processing is handled by the bpGetGIFDataBlock and bpSendACK methods.
Example:
{...}
if (RecdChar = cDLE) then
if OurBPFTP.bpDLESeen then ;
{...}
See also:
BPProtocol.bpDLESeen, BPProtoFTP.bpDLESeen
function bpGetGIFDataBlock(var P;
var PSize : Word;
var IsLast : Boolean) : Boolean;
Purpose:
Return a block data from the protocol handler for decoding. P is a var
parameter denoting a buffer at least large enough to hold one full packet's
worth of data. On exit, PSize holds the number of bytes placed in P, and
the IsLast parameter is set True if the packet received was a "TC"
termination packet or False otherwise.
The function returns False if the protocol terminates (a Failure packet is
received or too many consecutive protocol errors occur), otherwise it
returns True. Defective packets are automatically NAKed within the
protocol, but _your_ code is responsible for sending the ACK for a good
packet (see bpSendACK above).
Example:
{...}
var DecodeBuffer : Array[1..1024] of Byte;
DecodeSize : Word;
IsLastPacket : Boolean;
{...}
if NOT bpGetGIFDataBlock(DecodeBuffer,DecodeSize,IsLastPacket) then
{handle error}
else
{decode/display image data now in DecodeBuffer}
See also:
The example code in the OFLGIF.PAS file included in the package.
(* EOF *)